package common

import (
	"encoding/hex"
	"encoding/json"
	"fmt"
	"reflect"

	"hundsun.com/hsl/hschain/protos/common"
	protosledger "hundsun.com/hsl/hschain/protos/ledger"
)

//NetworkChainAccountConfig 需要暴露网络地址的区块链账号配置
type NetworkChainAccountConfig struct {
	Address string `yaml:"address"`
	PeerID  string `yaml:"peerID"`
}

//Init ...
func (m *NetworkChainAccountConfig) Init(configKey string, payload interface{}) error {
	if reflect.TypeOf(payload).Kind() != reflect.Map {
		return fmt.Errorf("type of config '%s' must be a map", configKey)
	}
	payloadMap := payload.(map[interface{}]interface{})
	addressObj, ok := payloadMap["address"]
	if !ok {
		return fmt.Errorf("field 'address' not found in config '%s'", configKey)
	}
	address, ok := addressObj.(string)
	if !ok {
		return fmt.Errorf("value type of field 'address' of config '%s' is not string", configKey)
	}

	peerIDObj, ok := payloadMap["peerID"]
	if !ok {
		return fmt.Errorf("field 'networkAddress' not found in config '%s'", configKey)
	}
	networkAddress, ok := peerIDObj.(string)
	if !ok {
		return fmt.Errorf("value type of field 'networkAddress' of config '%s' is not string", configKey)
	}

	m.Address = address
	m.PeerID = networkAddress
	return nil
}

//ConvertToProtoMessage ...
func (m *NetworkChainAccountConfig) ConvertToProtoMessage() (*common.NetworkChainAccount, error) {
	return &common.NetworkChainAccount{
		AccountAddress: m.Address,
		PeerID:         m.PeerID,
	}, nil
}

//SequenceCenterConfig ...
type SequenceCenterConfig struct {
	PeerID        string `yaml:"peerID"`
	SignerAddress string `yaml:"signerAddress"`
}

//BalanceAllocConfig ...
type BalanceAllocConfig struct {
	Address string `yaml:"address"`
	Balance uint64 `yaml:"balance"`
}

//HexHash ...
type HexHash []byte

//MarshalJSON mashal json
func (h HexHash) MarshalJSON() ([]byte, error) {
	x := hex.EncodeToString(h)
	return json.Marshal(x)
}

//ExecutedBlock ...
type ExecutedBlock struct {
	Block           *Block           `json:"block"`
	BlockExecResult *BlockExecResult `json:"block_exec_result"`
}

//FromPbExecutedBlock copy result
func FromPbExecutedBlock(execBlk *protosledger.ExecutedBlock) *ExecutedBlock {
	result := &ExecutedBlock{
		Block:           FromPbBlock(execBlk.Block),
		BlockExecResult: FromPbBlockExecResult(execBlk.BlockExecResult),
	}
	return result
}

//BlockExecResult copy of ledger.BlockExecResult
type BlockExecResult struct {
	BlockNumber            uint64                   `json:"block_number"`
	TransactionExecResults []*TransactionExecResult `json:"transaction_exec_results"`
	StateHash              HexHash                  `json:"stateHash"`
}

//FromPbBlockExecResult copy result
func FromPbBlockExecResult(blkExecResult *protosledger.BlockExecResult) *BlockExecResult {
	arr := make([]*TransactionExecResult, len(blkExecResult.TransactionExecResults))
	for i := range blkExecResult.TransactionExecResults {
		arr[i] = FromPbTxExecResult(blkExecResult.TransactionExecResults[i])
	}
	result := &BlockExecResult{
		BlockNumber:            blkExecResult.BlockNumber,
		TransactionExecResults: arr,
		StateHash:              blkExecResult.StateHash,
	}

	return result
}

//Block copy of ledger.Block
type Block struct {
	Header       *BlockHeader   `json:"header"`
	Transactions []*Transaction `json:"transactions"`
}

//FromPbBlock copy result
func FromPbBlock(blk *protosledger.Block) *Block {
	txs := make([]*Transaction, len(blk.Transactions))
	for i := range blk.Transactions {
		txs[i] = FromPbTransaction(blk.Transactions[i])
	}
	result := &Block{
		Header:       FromPbBlockHeader(blk.Header),
		Transactions: txs,
	}
	return result
}

//Signature ...
type Signature struct {
	Algorithm uint32  `json:"algorithm"`
	PublicKey HexHash `json:"publicKey"`
	Signature HexHash `json:"signature"`
}

//FromPbSignature copy result
func FromPbSignature(sig *common.Signature) *Signature {
	if sig == nil {
		return nil
	}
	result := &Signature{
		Algorithm: sig.Algorithm,
		PublicKey: sig.PublicKey,
		Signature: sig.Signature,
	}
	return result
}

//BlockHeader copy of ledger.BlockHeader
type BlockHeader struct {
	// 前一区块hash
	ParentHash HexHash `json:"parent_hash"`
	// 区块号
	Number uint64 `json:"number"`
	// 出块难度系数
	Difficulty uint64 `json:"difficulty"`
	// 出块时间
	BlockTime int64 `json:"block_time"`
	// 交易根hash
	TxRootHash HexHash `json:"txRootHash"`
	// 检查块的编号
	CheckBlock uint64 `json:"check_block"`
	// 检查块状态哈希
	StateHashOfCheckBlock HexHash `json:"state_hash_of_check_block"`
	// 矿工签名
	Signature              *Signature `json:"signature"`
	BlockSequence          uint64     `json:"block_sequence"`
	BlockSequenceSignature *Signature `json:"block_sequence_signature"`
	// 共识算法相关字段（不参与区块哈希计算）
	Consensus []byte `json:"consensus"`
	// 表示区块所属链
	ChainID string `json:"chain_id"`
}

//FromPbBlockHeader copy result
func FromPbBlockHeader(blockHeader *protosledger.BlockHeader) *BlockHeader {
	if blockHeader == nil {
		return nil
	}
	result := &BlockHeader{
		ParentHash:             blockHeader.ParentHash,
		Number:                 blockHeader.Number,
		Difficulty:             blockHeader.Difficulty,
		BlockTime:              blockHeader.BlockTime,
		TxRootHash:             blockHeader.TxRootHash,
		CheckBlock:             blockHeader.CheckBlock,
		StateHashOfCheckBlock:  blockHeader.StateHashOfCheckBlock,
		Signature:              FromPbSignature(blockHeader.Signature),
		BlockSequence:          blockHeader.BlockSequence,
		BlockSequenceSignature: FromPbSignature(blockHeader.BlockSequenceSignature),
		Consensus:              blockHeader.Consensus,
		ChainID:                blockHeader.ChainId,
	}
	return result
}

//ContractIdentifier copy of common.ContractIdentifier
type ContractIdentifier struct {
	Name    string `json:"name"`
	Version string `json:"version"`
}

//FromPbContractID copy result
func FromPbContractID(contractID *common.ContractIdentifier) *ContractIdentifier {
	if contractID == nil {
		return nil
	}
	result := &ContractIdentifier{
		Name:    contractID.Name,
		Version: contractID.Version,
	}
	return result
}

//Transaction copy of ledger.Transaction
type Transaction struct {
	Ty         string              `json:"ty"`
	From       string              `json:"from"`
	To         string              `json:"to"`
	ContractID *ContractIdentifier `json:"contract_id"`
	Value      uint64              `json:"value"`
	Fee        uint64              `json:"fee"`
	Payload    HexHash             `json:"payload"`
	Nonce      uint64              `json:"nonce"`
	// 交易创建时间（毫秒格式unix时间戳）
	CreateTime int64 `json:"createTime"`
	// 交易有效时间（单位秒）
	TTL uint32 `json:"TTL"`
	// 交易签名
	Signature *Signature `json:"signature"`
	// 标识交易所属链
	ChainID string `json:"chainID"`
}

//FromPbTransaction copy result
func FromPbTransaction(tx *protosledger.Transaction) *Transaction {
	if tx == nil {
		return nil
	}
	result := &Transaction{
		Ty:         tx.Ty,
		From:       tx.From,
		To:         tx.To,
		ContractID: FromPbContractID(tx.ContractId),
		Value:      tx.Value,
		Fee:        tx.Fee,
		Payload:    tx.Payload,
		Nonce:      tx.Nonce,
		CreateTime: tx.CreateTime,
		TTL:        tx.TTL,
		Signature:  FromPbSignature(tx.Signature),
		ChainID:    tx.ChainID,
	}
	return result
}

//KeyValue copy of common.KeyValue
type KeyValue struct {
	Key   HexHash `json:"key"`
	Value HexHash `json:"value"`
}

//FromPbKeyValue copy result
func FromPbKeyValue(kv *common.KeyValue) *KeyValue {
	if kv == nil {
		return nil
	}
	result := &KeyValue{
		Key:   kv.Key,
		Value: kv.Value,
	}
	return result
}

//TransactionExecResult copy of ledger.TransactionExecResult
type TransactionExecResult struct {
	Receipt *TransactionReceipt `json:"receipt"`
	Kvs     []*KeyValue         `json:"kvs"`
}

//FromPbTxExecResult copy result
func FromPbTxExecResult(txExecResult *protosledger.TransactionExecResult) *TransactionExecResult {
	if txExecResult == nil {
		return nil
	}
	kvs := make([]*KeyValue, len(txExecResult.Kvs))
	for i := range kvs {
		kvs[i] = FromPbKeyValue(txExecResult.Kvs[i])
	}
	result := &TransactionExecResult{
		Receipt: FromPbTxReceipt(txExecResult.Receipt),
		Kvs:     kvs,
	}
	return result
}

//ContractEvent copy of common.ContractEvent
type ContractEvent struct {
	TransactionHash    HexHash             `json:"transaction_hash"`
	ContractID         *ContractIdentifier `json:"contract_id"`
	ContractAddress    string              `json:"contract_address"`
	BlockNumber        uint64              `json:"block_number"`
	Name               string              `json:"name"`
	IndexedParamFirst  string              `json:"indexed_param_first"`
	IndexedParamSecond string              `json:"indexed_param_second"`
	IndexedParamThird  string              `json:"indexed_param_third"`
	Data               HexHash             `json:"data"`
}

//FromPbContractEvent copy result
func FromPbContractEvent(event *common.ContractEvent) *ContractEvent {
	if event == nil {
		return nil
	}
	result := &ContractEvent{
		TransactionHash:    event.TransactionHash,
		ContractID:         FromPbContractID(event.ContractId),
		ContractAddress:    event.ContractAddress,
		BlockNumber:        event.BlockNumber,
		Name:               event.Name,
		IndexedParamFirst:  event.IndexedParamFirst,
		IndexedParamSecond: event.IndexedParamSecond,
		IndexedParamThird:  event.IndexedParamThird,
		Data:               event.Data,
	}
	return result
}

//TransactionReceipt copy of ledger.TransactionReceipt
type TransactionReceipt struct {
	TxHash           string           `json:"tx_hash"`
	BlockNumber      uint64           `json:"block_number"`
	TransactionIndex int32            `json:"transaction_index"`
	TxStatus         string           `json:"tx_status"`
	Events           []*ContractEvent `json:"events"`
	ContractAddress  string           `json:"contract_address"`
	CallResult       HexHash          `json:"call_result"`
	FromBalance      uint64           `json:"from_balance"`
	// 合约Id
	ContractID *ContractIdentifier `json:"contract_id"`
	// 可存回执中附加的信息
	Logs []*TransactionReceiptLog `json:"logs"`
}

//FromPbTxReceipt copy result
func FromPbTxReceipt(txReceipt *protosledger.TransactionReceipt) *TransactionReceipt {
	if txReceipt == nil {
		return nil
	}
	events := make([]*ContractEvent, len(txReceipt.Events))
	for i := range events {
		events[i] = FromPbContractEvent(txReceipt.Events[i])
	}

	result := &TransactionReceipt{
		TxHash:           hex.EncodeToString(txReceipt.TxHash),
		BlockNumber:      txReceipt.BlockNumber,
		TransactionIndex: txReceipt.TransactionIndex,
		TxStatus:         txReceipt.TxStatus.String(),
		Events:           events,
		ContractAddress:  txReceipt.ContractAddress,
		CallResult:       txReceipt.CallResult,
		FromBalance:      txReceipt.FromBalance,
		ContractID:       FromPbContractID(txReceipt.ContractId),
		Logs:             FromPbTxReceiptLogs(txReceipt.Logs),
	}
	return result
}

// TransactionReceiptLog transaction receipt log
type TransactionReceiptLog struct {
	Ty   string `json:"ty,omitempty"`
	Data []byte `json:"data,omitempty"`
}

// FromPbTxReceiptLogs copy receipt logs
func FromPbTxReceiptLogs(receiptLogs []*protosledger.Log) []*TransactionReceiptLog {
	if receiptLogs == nil {
		return nil
	}

	var logs []*TransactionReceiptLog
	for _, log := range receiptLogs {
		logs = append(logs, &TransactionReceiptLog{Ty: TyLogMap[log.GetTy()], Data: log.GetData()})
	}

	return logs
}

//ExecutedTransaction copy of ledger.ExecutedTransaction
type ExecutedTransaction struct {
	Tx      *Transaction        `json:"tx"`
	Receipt *TransactionReceipt `json:"receipt"`
}

//FromPbExecTransaction copy result
func FromPbExecTransaction(execTx *protosledger.ExecutedTransaction) *ExecutedTransaction {
	if execTx == nil {
		return nil
	}
	result := &ExecutedTransaction{
		Tx:      FromPbTransaction(execTx.Tx),
		Receipt: FromPbTxReceipt(execTx.Receipt),
	}
	return result
}
